﻿#region Imports

using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using Remotion.Linq;
using Remotion.Linq.Clauses;
using Remotion.Linq.Clauses.ResultOperators;

#endregion

namespace CITPRO.Linq.LinqToOdbc.SqlQueryGeneration {
    internal class QueryModelVisitor : QueryModelVisitorBase {
        private QueryPartsAggregator queryParts;
        private IDialectProvider dialectProvider;


        public static string GenerateSqlQuery(QueryModel queryModel, IDialectProvider dialectProvider, QueryPartsAggregator queryPartsAggregator = null) {

            var visitor = new QueryModelVisitor {
                queryParts = new QueryPartsAggregator(queryPartsAggregator),
                dialectProvider = dialectProvider
            };
            visitor.VisitQueryModel(queryModel);
            var sql = visitor.queryParts.BuildSqlString();
            Debug.WriteLine(sql, "SQL");
            return sql;
        }

        public override void VisitQueryModel(QueryModel queryModel) {

            queryModel.SelectClause.Accept(this, queryModel);
            queryModel.MainFromClause.Accept(this, queryModel);
            VisitBodyClauses(queryModel.BodyClauses, queryModel);
            VisitResultOperators(queryModel.ResultOperators, queryModel);
        }

        public override void VisitResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, int index) {
            Debug.WriteLine("VisitResultOperator");
            Debug.Indent();
            Debug.Print(resultOperator.GetType().Name);
            if (resultOperator is ContainsResultOperator) {
                var containsResultOp = (ContainsResultOperator)resultOperator;
                VisitExpression(containsResultOp.Item);
            } else {
                base.VisitResultOperator(resultOperator, queryModel, index);
            }
            Debug.Unindent();
        }

        public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel) {
            Debug.WriteLine("VisitMainFromClause");
            Debug.Indent();
            Debug.WriteLine(fromClause.ItemType.Name, "Entity");
            Debug.WriteLine(fromClause.ItemName, "Alias");
            //
            // Handle IN clause (based on array input)
            if (fromClause.FromExpression.Type.IsArray) {
                queryParts.State = QueryPartsState.VisitingInClause;
                VisitExpression(fromClause.FromExpression);
            } else {
                queryParts.State = QueryPartsState.VisitingMainFromClause;
                queryParts.AddFromTable(fromClause.ItemType, fromClause.ItemName);
            }


            Debug.Unindent();
            base.VisitMainFromClause(fromClause, queryModel);
        }

        public override void VisitSelectClause(SelectClause selectClause, QueryModel queryModel) {
            queryParts.State = QueryPartsState.VisitingSelectClause;

            Debug.WriteLine("VisitSelectClause");
            Debug.Indent();
            VisitExpression(selectClause.Selector);
            Debug.Unindent();

            base.VisitSelectClause(selectClause, queryModel);
        }

        public override void VisitWhereClause(WhereClause whereClause, QueryModel queryModel, int index) {
            queryParts.State = QueryPartsState.VisitingWhereClause;
            Debug.WriteLine("VisitWhereClause");
            Debug.Indent();
            VisitExpression(whereClause.Predicate);
            Debug.Unindent();
            base.VisitWhereClause(whereClause, queryModel, index);
        }

        private void VisitExpression(Expression expression) {
            ExpressionTreeVisitor.EvaluateExpression(expression, queryParts, dialectProvider);
        }


    }
}